package features.advance.ch3;

import javax.swing.*;
import java.awt.*;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.channels.SocketChannel;
import java.util.Scanner;

public class InterruptibleSocketTest {

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                JFrame frame = new InterruptibleSocketFrame();
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setVisible(true);
            }
        });

    }
}
    class InterruptibleSocketFrame extends JFrame {
        private Scanner in;
        private JButton interruptibleButton;
        private JButton blockingButton;
        private JButton cancelButton;
        private JTextArea messages;
        private TestServer server;
        private Thread connectThread;
        public static final int WIDTH = 300;
        public static final int HEIGHT = 300;

        public InterruptibleSocketFrame() {
            setSize(WIDTH, HEIGHT);
            setTitle("InterruptibleSocketTest");

            JPanel northPanel = new JPanel();
            add(northPanel, BorderLayout.NORTH);

            messages = new JTextArea();
            add(new JScrollPane(messages));

            interruptibleButton = new JButton("Interruptible");
            blockingButton = new JButton("Blocking");
            northPanel.add(interruptibleButton);
            northPanel.add(blockingButton);
            interruptibleButton.addActionListener((event) -> {
                interruptibleButton.setEnabled(false);
                blockingButton.setEnabled(false);
                cancelButton.setEnabled(true);
                connectThread = new Thread(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            connectInterruptibly();
                        } catch (Exception e) {
                            messages.append("\nInterruptibleSocketTest.connectInterruptibly: " + e);
                        }
                    }
                });
                connectThread.start();
            });
            blockingButton.addActionListener((event) -> {
                interruptibleButton.setEnabled(false);
                blockingButton.setEnabled(false);
                cancelButton.setEnabled(true);
                connectThread = new Thread(() -> {
                    try {
                        connectBlocking();
                    } catch (Exception e) {
                        messages.append("\nInterruptibleSocketTest.connectBlocking: " + e);
                    }
                });
                connectThread.start();
            });
            cancelButton = new JButton("Cancel");
            cancelButton.setEnabled(false);
            northPanel.add(cancelButton);
            cancelButton.addActionListener((event) -> {
                connectThread.interrupt();
                cancelButton.setEnabled(false);
            });
            server = new TestServer();
            new Thread(server).start();
        }

        /**
         * Connects to the test server,using blocking I/O
         * 阻塞的
         */
        public void connectBlocking() throws IOException {
            // TODO: 2019/12/1 0001
            messages.append("Blocking:\n");
            Socket sock = new Socket("localhost", 8189);
            try {
                in = new Scanner(sock.getInputStream());
                while (!Thread.currentThread().isInterrupted()) {
                    messages.append("Reading ");
                    if (in.hasNextLine()) {
                        String line = in.nextLine();
                        messages.append(line);
                        messages.append("\n");
                    }
                }
            }catch (Exception e){
                e.printStackTrace();
            } finally {
                sock.close();
                EventQueue.invokeLater(() -> {
                    messages.append("Socket closed\n");
                    interruptibleButton.setEnabled(true);
                    blockingButton.setEnabled(true);
                });
            }

        }

        /**
         * Connect to the test server,using interruptible I/O
         * 可中断的
         */
        public void connectInterruptibly() throws IOException {
            messages.append("Interruptible:\n");
            //获取连接当前地址和端口的channel
            SocketChannel channel = SocketChannel.open(new InetSocketAddress("localhost", 8189));
            try {
                //获取输入流
                in = new Scanner(channel);
                //当线程不是阻断的
                while (!Thread.currentThread().isInterrupted()) {
                    messages.append("Reading ");//拼接Reading
                    if (in.hasNextLine()) {//如果有输入内容
                        String line = in.nextLine();//获取输入内容
                        messages.append(line);//拼接输入内容
                        messages.append("\n");//换行
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                channel.close();
                EventQueue.invokeLater(() -> {
                    messages.append("Channel closed\n");
                    interruptibleButton.setEnabled(true);
                    blockingButton.setEnabled(true);
                });
            }
        }


    /**
     * 一个多线程服务器，监听8189端口并且可以发送数字到客户端
     * 模拟一个发送10个数字就挂起的服务器
     */
    class TestServer implements Runnable {
        @Override
        public void run() {
            try {
                ServerSocket s = new ServerSocket(8189);
                while (true) {
                    Socket incoming = s.accept();
                    Runnable r = new TestServerHandler(incoming);
                    Thread t = new Thread(r);
                    t.start();
                }
            } catch (IOException e) {
                messages.append("\nTestServer.run: " + e);
            } finally {

            }
        }
    }

        /**
         * 这个类专门处理一个服务器套接字链接的客户端输入
         */
    class TestServerHandler implements Runnable {

            private Socket incoming;
            private int counter;
            /**
         *构造一个处理器
         * i 套接字
         */
        public TestServerHandler(Socket incoming) {
            this.incoming = incoming;
        }
        @Override
        public void run() {
            try{
                OutputStream outStream = incoming.getOutputStream();
                PrintWriter out = new PrintWriter(outStream, true/*autoFlush*/);
                while (counter < 100){
                    counter++;
                    if (counter <= 10)out.println(counter);
                    Thread.sleep(100);
                }
                incoming.close();
                messages.append("Closing server\n");
            }catch (Exception e){
                messages.append("\nTestServerHandler.run:"+e);
            }
        }
    }
}